/*************************************************************************
|                                                                        |
|   MACRO.C                                                     11.06.93 |
|   ADMAKE Utility:  macro control functions                             |
|                                                                        |
*************************************************************************/

#ifdef VMS
#include STDIO
#include STRING
#include STDLIB
#include "h.h"
#else
#include <stdio.h>
#include <string.h>
#ifdef HEAP
#include <heap.h>
#endif
#include "h.h"
#include <stdlib.h>
#endif

struct macro *          macrohead;

/*************************************************************************
|                                                                        |
|   >>>>  getmp                                                          |
|                                                                        |
|   get macro, do substitutions                                          |
|                                                                        |
*************************************************************************/

#ifdef PANEWFD
struct macro * getmp(char * name)
#else
struct macro * getmp(name)
char * name;
#endif

{
    register struct macro * rp;
    char *                  p;
    char *                  q;
    char *                  qq;
    char *                  qqq;
    char *                  sub1 = NULL;
    char *                  sub2 = NULL;
    char *                  a;
#ifdef SPLITPATH
    char                    drive[_MAX_DRIVE];
    char                    dir[_MAX_DIR];
    char                    fnam[_MAX_FNAME];
    char                    ext[_MAX_EXT];
#endif

    p = strchr(name,':');
    if (p)
    {
        sub1 = p+1;
        *p = '\0';
        sub2 = strchr(sub1,'=');
        if (sub2) *sub2++ = '\0';
        else sub2 = "";
    }

    for (rp = macrohead; rp; rp = rp->m_next)
        if (strcmp(name, rp->m_name) == 0)
        {
            rp->m_sub = NULL;
            if (sub1)
            {
#ifdef SPLITPATH
                if (sub1[0] == '^')
                {
                    _splitpath(rp->m_val,drive,dir,fnam,ext);
                    /*lint -e744 */
                    switch (sub1[1] & 0x5f)
                    {
                        case 'D': 
                                    rp->m_sub = malloc(strlen(drive) + 
                                                       strlen(dir) + 1);  
                                    if (!rp->m_sub) fatal(MSG[71]);
                                    p = psa(rp->m_sub,drive);
                                    p = psa(p,dir);
                                    break;

                        case 'F':
                                    rp->m_sub = malloc(strlen(fnam) + 
                                                       strlen(ext) + 1);  
                                    if (!rp->m_sub) fatal(MSG[71]);
                                    p = psa(rp->m_sub,fnam);
                                    p = psa(p,ext);
                                    break;

                        case 'B':
                                    rp->m_sub = malloc(strlen(fnam) + 1);
                                    if (!rp->m_sub) fatal(MSG[71]);
                                    p = psa(rp->m_sub,fnam);
                                    break;

                        case 'R':
                                    rp->m_sub = malloc(strlen(drive) + 
                                                       strlen(dir) +
                                                       strlen(fnam) + 1);
                                    if (!rp->m_sub) fatal(MSG[71]);
                                    p = psa(rp->m_sub,drive);
                                    p = psa(p,dir);
                                    p = psa(p,fnam);
                                    break;

                        case 'E':
                                    rp->m_sub = malloc(strlen(ext) + 1);
                                    if (!rp->m_sub) fatal(MSG[71]);
                                    p = psa(rp->m_sub,ext);
                                    break;
                    }
                    /*lint -restore */
                    return rp;
                }
#endif
                a = malloc(LZ);
                if (!a) fatal(MSG[71]);
                p = a;
                q = rp->m_val;
                while ((qq = pstrstr(q,sub1)) != (char *)0)
                {
                    for (qqq = q; qqq < qq;) *p++ = *qqq++;
                    q = qq + strlen(sub1);
                    qqq = sub2;
                    while (*qqq) *p++ = *qqq++;
                }
                if (q != rp->m_val)
                {
                    strcpy(p,q);
                    rp->m_sub = strdup(a);
                    if (!rp->m_sub) fatal(MSG[71]);
                }
                free(a);
            }
            return rp;
        }
    return (struct macro *)0;
}

/*************************************************************************
|                                                                        |
|   >>>>  doexp                                                          |
|                                                                        |
|   Do the dirty work for expand                                         |
|                                                                        |
*************************************************************************/

#ifdef PANEWFD
static VOID doexp(char * * to, char * from, int * len, char * buf)
#else
static VOID doexp(to, from, len, buf)
char * * to;
char * from;
int * len;
char * buf;
#endif

{
    register char *         rp;
    register char *         p;
    register char *         q;
    register struct macro * mp;

    rp = from;
    p = *to;
    while (*rp)
    {
        if (*rp != '$')
        {
            *p++ = *rp++;
            (*len)--;
        }
        else
        {
            q = buf;
            if (*++rp == '{')
                while (*++rp && *rp != '}')
                    *q++ = *rp;
            else if (*rp == '(')
                while (*++rp && *rp != ')')
                    *q++ = *rp;
            else if (!*rp)
            {
                *p++ = '$';
                break;
            }
            else
                *q++ = *rp;
            *q = '\0';
            if (*rp)
                rp++;
            if ((mp = getmp(buf)) == NULL)
                mp = setmacro(buf, "");
            if (mp->m_flag & M_LOOPCHK)
                fatal(MSG[72], mp->m_name); /* infinite recursion */
            mp->m_flag |= M_LOOPCHK;
            *to = p;
            if (mp->m_sub)
            {
                doexp(to, mp->m_sub, len, buf);
                free(mp->m_sub);
            }
            else
                doexp(to, mp->m_val, len, buf);
            p = *to;
            mp->m_flag &= ~M_LOOPCHK;
        }
        if (*len <= 0)
            error(MSG[73]);             /* Macro expansion too long */
    }
    *p = '\0';
    *to = p;
}

/*************************************************************************
|                                                                        |
|   >>>>  expand                                                         |
|                                                                        |
|   expand any macros in str                                             |
|                                                                        |
*************************************************************************/

#ifdef PANEWFD
VOID expand(char * str)
#else
VOID expand(str)
char * str;
#endif

{
    static char             a[LZ];
    static char             b[LZ];
    char *                  p = str;
    int                     len = LZ-1;

    strcpy(a, str);
    doexp(&p, a, &len, b);
}

/*************************************************************************
|                                                                        |
|   markmacros                                                           |
|                                                                        |
|   mark all M_ENV macros as permanent (M_PERM)                          |
|                                                                        |
*************************************************************************/

#ifdef PANEWFD
VOID markmacros(void)
#else
VOID markmacros()
#endif

{
    register struct macro * rp;

    for (rp = macrohead; rp; rp = rp->m_next)
    {
        if (rp->m_flag & M_ENV) rp->m_flag |= M_PERM;
    }
}

/*************************************************************************
|                                                                        |
|   >>>>  setmacro                                                       |
|                                                                        |
|   set macro value                                                      |
|                                                                        |
*************************************************************************/

#ifdef PANEWFD
struct macro * setmacro(char * name, char * val)
#else
struct macro * setmacro(name, val)
char * name;
char * val;
#endif

{
    register struct macro * rp;
    register char *         cp;
    register char *         q;
    register int            i,j;
    struct path *           pp;
    static char             buf[LZ];
    char c;

    if (*name == '+') 
    {
        name++;
        if ( (strlen(name) + strlen(val)) < 126)
        {
            for (i = 0, j = 0; name[j]; )
            {
                c = *(name+(j++));
                if ((c >= 'a') && (c <= 'z')) c += 'A' - 'a';
                buf[i++] = c;
            }
            buf[i++] = '=';
            for (j = 0; *(val+j); ) buf[i++] = *(val+(j++));
            buf[i++] = '\0';
            expand(buf);
#ifndef VMS
            if ((cp = strdup(buf)) == (char *)0) fatal(MSG[69]);
            putenv(cp);
#endif            
        }
    }

/*****  replace macro definition if it exists  */
/*****  don't let macros derived from external env. be replaced if -e given */

    for (rp = macrohead; rp; rp = rp->m_next)
        if (strcmp(name, rp->m_name) == 0)
        {
            if (rp->m_flag & M_PERM) return rp;
            free(rp->m_val);        /*  Free space from old  */
            break;
        }

    if (!rp)                /*  If not defined, allocate space for new  */
    {
        if ((rp = (struct macro *)malloc(sizeof (struct macro)))
            == (struct macro *)0) fatal(MSG[69]);
        rp->m_next = macrohead;
        macrohead = rp;
        rp->m_sub = NULL;
        if ((cp = malloc(strlen(name)+1)) == (char *)0) fatal(MSG[69]);
        strcpy(cp, name);
        rp->m_name = cp;
    }

    if ((cp = malloc(strlen(val)+1)) == (char *)0) fatal(MSG[69]);
    strcpy(cp, val);                /*  Copy in new value  */
    rp->m_val = cp;
    rp->m_flag = macrotype;

    if (!strncmp(name,".PATH.",6))
    {
        q = suffix(name + 1);

        for (pp = pathhead; pp; pp = pp->p_next)
            if (strcmp(q, pp->p_ext) == 0)
            {
                free(pp->p_path);
                break;
            }
    
        if (!pp)
        {
            if ((pp = (struct path *)malloc(sizeof (struct path)))
                == (struct path *)0) fatal(MSG[70]);
            pp->p_next = pathhead;
            pathhead = pp;
            if ((cp = malloc(strlen(q)+1)) == (char *)0) fatal(MSG[70]);
            strcpy(cp, q);
            pp->p_ext = cp;
        }

        if ((cp = malloc(strlen(val)+1)) == (char *)0) fatal(MSG[70]);
        strcpy(cp, val);
        pp->p_path = cp;
    }

    return rp;
}

/*************************************************************************
|                                                                        |
|   >>>>  addtomacro                                                     |
|                                                                        |
|   concatenate value to macro                                           |
|                                                                        |
*************************************************************************/

#ifdef PANEWFD
struct macro * addtomacro(char * name, char * val)
#else
struct macro * addtomacro(name, val)
char * name;
char * val;
#endif

{
    char *                  nam;
    register struct macro * rp;
    register char *         cp;

    nam = (*name == '+') ? name + 1 : name;

    for (rp = macrohead; rp; rp = rp->m_next)
        if (strcmp(nam, rp->m_name) == 0) break;
    if (!rp) return setmacro(name, val);

    if ((cp = malloc(strlen(val) + 1 + strlen(rp->m_val))) == (char *) 0)
        fatal(MSG[69]);
    expand(val);
    psa(psa(cp,rp->m_val), val);
    rp = setmacro(name, cp);
    free(cp);
    return rp;
}

/*************************************************************************
|                                                                        |
|   >>>>  extmacro                                                       |
|                                                                        |
|   extract substring from macro                                         |
|                                                                        |
*************************************************************************/

#ifdef PANEWFD
struct macro * extmacro(char * name, char * val)
#else
struct macro * extmacro(name, val)
char * name;
char * val;
#endif

{
    char *                  nam;
    register struct macro * rp;
    register char *         q;
    int                     l;

    nam = (*name == '+') ? name + 1 : name;

    for (rp = macrohead; rp; rp = rp->m_next)
        if (strcmp(nam, rp->m_name) == 0) break;
    if (!rp) return (struct macro *)0;
    expand(val);
    if ((q = pstrstr(rp->m_val, val)) == (char *)0) return (struct macro *)0;
    l = strlen(val);
    while ((*q = *(q + l)) != '\0') q++;
    if ((q = strdup(rp->m_val)) == (char *)0) fatal(MSG[69]);
    rp = setmacro(name, q);
    free(q);
    return rp;
}
